package step

import (
	"fmt"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type TXSignJR_JZ_StepUni struct {
	BaseMpcStep
	signNum int
	indexes []big.Int
}

func CreateTXSignJR_JZ_StepUni(degree int, totalNum int, signNum int) *TXSignJR_JZ_StepUni {
	log.SyslogInfo(fmt.Sprintf("TXSignJR_JZ_StepUni, degree:%d, signNum:%d, peerNum:%d", degree, signNum, totalNum))

	mpc := &TXSignJR_JZ_StepUni{*CreateBaseMpcStep(totalNum, 4*signNum), signNum, []big.Int{}}
	for i := 0; i < signNum; i++ {
		mpc.messages[4*i+0] = createJRSSValueUni(degree, totalNum)
		mpc.messages[4*i+1] = createJRSSValueUni(degree, totalNum)
		mpc.messages[4*i+2] = createJZSSValueUni(degree*2, totalNum)
		mpc.messages[4*i+3] = createJZSSValueUni(degree*2, totalNum)
	}

	return mpc
}

func (jrjz *TXSignJR_JZ_StepUni) GetMsgGens() []MpcMessageGenerator {
	return jrjz.messages
}

func (jrjz *TXSignJR_JZ_StepUni) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("TXSignJR_JZ_StepUni::CreateMessage begin", "ctx", jrjz.GetMpcContextId())

	message := make([]mpcprotocol.StepMessage, jrjz.totalNum)

	_, grpId, _ := jrjz.BaseStep.GetGrpId()
	for i := 0; i < jrjz.totalNum; i++ {
		message[i].MsgCode = mpcprotocol.MPCMessage

		nodeId, _ := osmconf.GetOsmConf().GetNodeIdByIndex(grpId, uint16(i))
		message[i].PeerID = nodeId
	}

	for i := 0; i < jrjz.signNum; i++ {
		a := jrjz.messages[4*i+0].(*RandomPolynomialValueUni)
		r := jrjz.messages[4*i+1].(*RandomPolynomialValueUni)
		b := jrjz.messages[4*i+2].(*RandomPolynomialValueUni)
		c := jrjz.messages[4*i+3].(*RandomPolynomialValueUni)

		for j := 0; j < jrjz.totalNum; j++ {
			message[j].Data = append(message[j].Data, []big.Int{a.polyValue[j], r.polyValue[j], b.polyValue[j], c.polyValue[j]}...)
		}
	}

	return message
}

func (jrjz *TXSignJR_JZ_StepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogInfo(fmt.Sprintf("..........TXSignJR_JZ_StepUni::HandleMessage, PeerID:%s, DataLen:%d, ctx=%v, StepId=%v, waiting=%d",
		msg.PeerID.SlimString(), len(msg.Data), jrjz.GetMpcContextId(), msg.StepId, jrjz.waiting))

	if len(msg.Data) != jrjz.signNum*4 {
		log.SyslogErr(fmt.Sprintf("TXSignJR_JZ_StepUni HandleMessage, received data len doesn't match requirement, dataLen:%d", len(msg.Data)), "ctx", jrjz.GetMpcContextId())
		return false
	}

	result := jrjz.mpcResult
	_, grpIdString, _ := jrjz.BaseStep.GetGrpId()
	senderIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, msg.PeerID)
	for i := 0; i < jrjz.signNum; i++ {
		a := jrjz.messages[4*i+0].(*RandomPolynomialValueUni)
		r := jrjz.messages[4*i+1].(*RandomPolynomialValueUni)
		b := jrjz.messages[4*i+2].(*RandomPolynomialValueUni)
		c := jrjz.messages[4*i+3].(*RandomPolynomialValueUni)
		_, exist := a.message[*msg.PeerID]
		if exist {
			log.SyslogWarning("TXSignJR_JZ_StepUni::HandleMessage, repeat resp. peer:%s", msg.PeerID.SlimString(), "ctx", jrjz.GetMpcContextId())
			return false
		}

		// save Jrjz data
		jrjzDataKey := mpcprotocol.JRJZData + "_" + strconv.Itoa(i) + "_" + strconv.Itoa(int(senderIndex))
		err := result.SetValue(jrjzDataKey, []big.Int{msg.Data[4*i+0], msg.Data[4*i+1], msg.Data[4*i+2], msg.Data[4*i+3]})
		if err != nil {
			log.SyslogErr("TXSignJR_JZ_StepUni err setValue", "key", jrjzDataKey, "ctx", jrjz.GetMpcContextId())
			return false
		}
		defer a.wrLock.Unlock()
		a.wrLock.Lock()
		a.message[*msg.PeerID] = msg.Data[4*i+0] //message.Value

		defer r.wrLock.Unlock()
		r.wrLock.Lock()
		r.message[*msg.PeerID] = msg.Data[4*i+1] //message.Value

		defer b.wrLock.Unlock()
		b.wrLock.Lock()
		b.message[*msg.PeerID] = msg.Data[4*i+2] //message.Value

		defer c.wrLock.Unlock()
		c.wrLock.Lock()
		c.message[*msg.PeerID] = msg.Data[4*i+3] //message.Value
	}

	indexBig := big.NewInt(0).SetUint64(uint64(senderIndex))
	jrjz.indexes = append(jrjz.indexes, *indexBig)

	return true
}

func (jrjz *TXSignJR_JZ_StepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	err := jrjz.BaseMpcStep.FinishStep()
	if err != nil {
		log.SyslogErr(fmt.Sprintf("TXSignJR_JZ_StepUni::BaseMpcStep.FinishStep fail, err:%s", err.Error()), "ctx", jrjz.GetMpcContextId())
		return err
	}

	for i := 0; i < jrjz.signNum; i++ {
		iStr := "_" + strconv.Itoa(i)
		a := jrjz.messages[4*i+0].(*RandomPolynomialValueUni)
		r := jrjz.messages[4*i+1].(*RandomPolynomialValueUni)
		b := jrjz.messages[4*i+2].(*RandomPolynomialValueUni)
		c := jrjz.messages[4*i+3].(*RandomPolynomialValueUni)
		err = result.SetValue(mpcprotocol.MpcSignA+iStr, []big.Int{*a.result})
		err = result.SetValue(mpcprotocol.MpcSignA0+iStr, []big.Int{a.randCoefficient[0]})
		err = result.SetValue(mpcprotocol.MpcSignR+iStr, []big.Int{*r.result})
		err = result.SetValue(mpcprotocol.MpcSignR0+iStr, []big.Int{r.randCoefficient[0]})
		err = result.SetValue(mpcprotocol.MpcSignB+iStr, []big.Int{*b.result})
		err = result.SetValue(mpcprotocol.MpcSignC+iStr, []big.Int{*c.result})

		ar := make([]big.Int, 1)
		ar[0].Mul(a.result, r.result)
		ar[0].Mod(&ar[0], crypto.Secp256k1_N)
		ar[0].Add(&ar[0], b.result)
		ar[0].Mod(&ar[0], crypto.Secp256k1_N)
		err = result.SetValue(mpcprotocol.MpcSignARSeed+iStr, ar)
	}

	bigCollection, _ := osmconf.BuildDataByIndexes(&jrjz.indexes)
	err1 := result.SetValue(mpcprotocol.JRJZCollection, []big.Int{*bigCollection})
	log.SyslogInfo("TXSignJR_JZ_StepUni::FinishStep", "bigCollection", hexutil.Encode(bigCollection.Bytes()), "ctx", jrjz.GetMpcContextId())
	if err != nil {
		return err1
	}

	log.SyslogDebug("TXSignJR_JZ_StepUni::FinishStep succeed", "ctx", jrjz.GetMpcContextId())
	return nil
}
